﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using Unity.Plastic.Newtonsoft.Json;
using System.Drawing.Printing;

public enum AssetBundleCompresionPattern
{
    LZMA,
    LZ4,
    None
}
public enum IncrementalBuildMode
{
    None,
    IncrementalBuild,
    ForcusRebuild
}
public class AssetBundleVersionDifference
{
    public List<string> AdditionAssetBundles;
    public List<string> ReducedAssetBundles;
}

public class AssetBundleEdge
{
    public List<AssetBundleNode> Nodes = new List<AssetBundleNode>();
}
public class AssetBundleNode
{
    public string assetName;
    public int SourceIndex = -1;
    public List<int> SourceIndices = new List<int>();


    public AssetBundleEdge OutEdge;
    public AssetBundleEdge InEdge;
}
public class AssetManager
{

    public static string AssetManagerVersion = "1.0.0";



    public static AssetBundlePattern BuildingPattern;


    //需要打包的文件夹
    public static DefaultAsset AssetBundleDirectory;


    public static IncrementalBuildMode _incrementalBuildMode;
    public static AssetBundleCompresionPattern CompressionPattern;


    //public static string MainAssetsBundleName = "AssetBundleOutput";
    //public static string AssetBundleOutputPath = Path.Combine(Application.persistentDataPath,"AssetBundleOutput");
    public static string AssetBundleOutputPath;




    public static DefaultAsset _AssetBundleDirectory;
    public static AsseetManagerConfigScirptableObject AssetManagerConfig;


    public static DefaultAsset assetbundledirectory
    {
        get => _AssetBundleDirectory;
        set
        {
            if (_AssetBundleDirectory != value)
            {
                _AssetBundleDirectory = value;

                GetCurrentDeirctoryAllAssets();

                Debug.Log("da");
            }

        }
    }

    public static List<string> CurrentAllAssets = new List<string>();
    public static int CurrentBundleVersion = 100;
    public static bool[] CurrentSelectAssets;


    public static string[] InvalidExtentionName = new string[] { ".meta", ".cs" };

    public const string AssetManagerName = nameof(AssetManager);
    [MenuItem(AssetManagerName + "/" + nameof(BuildAssetBundle))]
    public static void BuildAssetBundle()
    {
        CheckBuildingOutputPath();



        //string assetBundleDirectory = "Assets/AssetBundles";//这是 AssetBundle 要输出到的目录。可以将其更改为所需的任何输出目录,比如工程外路径，只需确保在尝试构建之前文件夹实际存在。

        if (Directory.Exists(AssetBundleOutputPath))
        {
            Directory.CreateDirectory(AssetBundleOutputPath);
            Debug.Log(AssetBundleOutputPath);
        }
        BuildPipeline.BuildAssetBundles(AssetBundleOutputPath,CheckCompressionPattern(),EditorUserBuildSettings.activeBuildTarget);

        //参数分别为打包路径，压缩格式，平台

        Debug.Log("AB包打包完成");


    }
    public static void LoadConfig()
    {

        if (AssetManagerConfig == null)
        {
            AssetManagerConfig = AssetDatabase.LoadAssetAtPath<AsseetManagerConfigScirptableObject>("Assets/Editor/AssetManagerconfig 1.asset");
        }

    }
    public static void SaveConfigToJson()
    {
        if (AssetManagerConfig != null)
        {
            string configString = JsonUtility.ToJson(AssetManagerConfig);

            string outputPath = Path.Combine(Application.dataPath, "Editor/AssetManagerConfig.amc");

            File.WriteAllText(outputPath, configString);

            AssetDatabase.Refresh();
        }
    }
    public static void LoadConfigFromJson()
    {
        string configPath = Path.Combine(Application.dataPath, "Editor/AssetManagerConfig.amc");

        string configString = File.ReadAllText(configPath);

        JsonUtility.FromJsonOverwrite(configString, AssetManagerConfig);

        Debug.Log(AssetManagerConfig.Name);
        Debug.Log(AssetManagerConfig.sampleVector);
    }

    [MenuItem(AssetManagerName + "/" + nameof(CreateConfig))]
    static void CreateConfig()
    {
        //    ScripttableObjectʵ  
        AsseetManagerConfigScirptableObject config = ScriptableObject.CreateInstance<AsseetManagerConfigScirptableObject>();


        AssetDatabase.CreateAsset(config, "Assets/Editor/AssetManagerConfig.asset");

        AssetDatabase.SaveAssets();

        AssetDatabase.Refresh();
    }

    static string ComputeAssetSetSignature(IEnumerable<string> assetNames)
    {
        var assetGuids = assetNames.Select(AssetDatabase.AssetPathToGUID);
        MD5 md5 = MD5.Create();
        foreach (string assetGuid in assetGuids.OrderBy(x => x))
        {
            byte[] buffer = Encoding.ASCII.GetBytes(assetGuid);

            md5.TransformBlock(buffer, 0, buffer.Length, null, 0);
        }
        md5.TransformFinalBlock(new byte[0], 0, 0);
        return BytesToHexString(md5.Hash);
    }
    static string BytesToHexString(byte[] bytes)
    {
        StringBuilder byteString = new StringBuilder();
        foreach (byte aByte in bytes)
        {
            byteString.Append(aByte.ToString("x2"));
        }
        return byteString.ToString();
    }

    public static AssetBundleVersionDifference ContrastAssetBundleVersion(string[] oldVersion, string[] newVersion)
    {
        AssetBundleVersionDifference diffrence = new AssetBundleVersionDifference();
        diffrence.AdditionAssetBundles = new List<string>();
        diffrence.ReducedAssetBundles = new List<string>();
        foreach (var assetName in oldVersion)
        {
            if (!newVersion.Contains(assetName))
            {
                diffrence.ReducedAssetBundles.Add(assetName);
            }
        }
        foreach (var assetName in newVersion)
        {
            if (!oldVersion.Contains(assetName))
            {
                diffrence.AdditionAssetBundles.Add(assetName);
            }
        }
        return diffrence;
    }

    static string[] BuildAssetBundleHashTable(AssetBundleBuild[] assetBundleBuilds)
    {
        string[] assetBundleHashs = new string[assetBundleBuilds.Length];

        for (int i = 0; i < assetBundleBuilds.Length; i++)
        {
            string assetBundlePath = Path.Combine(AssetBundleOutputPath, assetBundleBuilds[i].assetBundleName);
            FileInfo info = new FileInfo(assetBundlePath);
            assetBundleHashs[i] = $"{info.Length}_{assetBundleBuilds[i].assetBundleName}";
        }

        string hashString = JsonConvert.SerializeObject(assetBundleHashs);
        string hashFilePath = Path.Combine(AssetBundleOutputPath, "AssetBundleHashs");
        File.WriteAllText(hashFilePath, hashString);
        return assetBundleHashs;
    }

    public static void BuildAssetBundleFromDirectedGraph()
    {

        CheckBuildingOutputPath();


        List<string> selectedAssets = GetAllSelectedAssets();
        List<AssetBundleNode> allNodes = new List<AssetBundleNode>();

        for (int i = 0; i < selectedAssets.Count; i++)
        {
            AssetBundleNode currentNode = new AssetBundleNode();
            currentNode.assetName = selectedAssets[i];
            currentNode.SourceIndex = i;
            currentNode.SourceIndices = new List<int> { currentNode.SourceIndex };
            currentNode.InEdge = new AssetBundleEdge();
            allNodes.Add(currentNode);
            GetNodesFromDependencies(currentNode, allNodes);
        }
        Dictionary<List<int>, List<AssetBundleNode>> assetBundleNodeDic = new Dictionary<List<int>, List<AssetBundleNode>>();
        foreach (var node in allNodes)
        {

            if (!assetBundleNodeDic.Keys.Contains(node.SourceIndices))
            {
                assetBundleNodeDic.Add(node.SourceIndices, new List<AssetBundleNode>());
            }
            if (assetBundleNodeDic.Keys.Contains(node.SourceIndices))
            {
                assetBundleNodeDic[node.SourceIndices].Add(node);
            }
        }
        AssetBundleBuild[] assetBundleBuilds = new AssetBundleBuild[assetBundleNodeDic.Count];
        int buildIndex = 0;

        foreach (var key in assetBundleNodeDic.Keys)
        {

            List<string> assetNames = new List<string>();
            foreach (AssetBundleNode node in assetBundleNodeDic[key])
            {
                assetNames.Add(node.assetName);
            }

            string[] assetNamesArray = assetNames.ToArray();

            assetBundleBuilds[buildIndex].assetBundleName = ComputeAssetSetSignature(assetNamesArray);

            assetBundleBuilds[buildIndex].assetNames = assetNames.ToArray();
            buildIndex++;
        }
        BuildPipeline.BuildAssetBundles(AssetBundleOutputPath, assetBundleBuilds, CheckCompressionPattern(),
              BuildTarget.StandaloneWindows);

        string[] currentVersionAssetHashs = BuildAssetBundleHashTable(assetBundleBuilds);


        GetVersionDiffrence(currentVersionAssetHashs);

        CopyAssetBundleToVersionFolder();
        CurrentBundleVersion++;
        AssetDatabase.Refresh();
    }

    static void CopyAssetBundleToVersionFolder()
    {
        string versionString = CurrentBundleVersion.ToString();
        for (int i = versionString.Length - 1; i >= 1; i--)
        {
            versionString = versionString.Insert(i, ".");
        }

        string assetBundleVersionPath = Path.Combine(Application.streamingAssetsPath, versionString, AssetBundleLoad.MainAssetsBundleName);

        if (!Directory.Exists(assetBundleVersionPath))
        {
            Directory.CreateDirectory(assetBundleVersionPath);
        }

        string[] assetNames = ReadAssetBundleHashTable(AssetBundleOutputPath);

        //   ƹ ϣ  
        string hashTableOrignPath = Path.Combine(AssetBundleOutputPath, "AssetBundleHashs");
        string hashTableVersionPath = Path.Combine(assetBundleVersionPath, "AssetBundleHashs");
        File.Copy(hashTableOrignPath, hashTableVersionPath);

        //        
        string mainBundlePath = Path.Combine(AssetBundleOutputPath, AssetBundleLoad.MainAssetsBundleName);
        string mainBundleVersionPath = Path.Combine(assetBundleVersionPath, AssetBundleLoad.MainAssetsBundleName);
        File.Copy(mainBundlePath, mainBundleVersionPath);

        foreach (var assetName in assetNames)
        {
            string assetHashName = assetName.Substring(assetName.IndexOf("_") + 1);

            string assetOrignPath = Path.Combine(AssetBundleOutputPath, assetHashName);

            string assetVersionPath = Path.Combine(assetBundleVersionPath, assetHashName);

            File.Copy(assetOrignPath, assetVersionPath, true);
        }


    }

    static BuildAssetBundleOptions CheckIncrementalMode()
    {
        BuildAssetBundleOptions option = BuildAssetBundleOptions.None;
        switch (_incrementalBuildMode)
        {
            case IncrementalBuildMode.None:
                break;
            case IncrementalBuildMode.IncrementalBuild:
                option = BuildAssetBundleOptions.DeterministicAssetBundle;
                break;
            case IncrementalBuildMode.ForcusRebuild:
                option = BuildAssetBundleOptions.ForceRebuildAssetBundle;
                break;
        }
        return option;
    }
    static string[] ReadAssetBundleHashTable(string outputPaht)
    {

        string VersionHashTablePath = Path.Combine(outputPaht, "AssetBundleHashs");

        string VersionHashString = File.ReadAllText(VersionHashTablePath);

        string[] VersionAssetHashs = JsonConvert.DeserializeObject<string[]>(VersionHashString);

        return VersionAssetHashs;
    }
    static void GetVersionDiffrence(string[] currentAssetHashs)
    {
        if (CurrentBundleVersion >= 101)
        {
            int lastVersion = CurrentBundleVersion - 1;
            string versionString = lastVersion.ToString();
            for (int i = versionString.Length - 1; i >= 1; i--)
            {
                versionString = versionString.Insert(i, ".");
            }
            var lastOutputPaht = Path.Combine(Application.streamingAssetsPath, versionString, AssetBundleLoad.MainAssetsBundleName);


            string[] lastVersionAssetHashs = ReadAssetBundleHashTable(lastOutputPaht);

            AssetBundleVersionDifference diffrence = ContrastAssetBundleVersion(lastVersionAssetHashs, currentAssetHashs);

            foreach (var assetName in diffrence.AdditionAssetBundles)
            {
                Debug.Log("ds");
            }

            foreach (var assetName in diffrence.ReducedAssetBundles)
            {
                Debug.Log("das");
            }
        }

    }

    public static List<string> GetAllSelectedAssets()
    {

        if (CurrentSelectAssets == null || CurrentSelectAssets.Length == 0)
        {
            return null;
        }
        List<string> selectedAssets = new List<string>();

        for (int i = 0; i < CurrentSelectAssets.Length; i++)
        {
            if (CurrentSelectAssets[i])
            {
                selectedAssets.Add(CurrentAllAssets[i]);
            }
        }
        
        return selectedAssets;
    }

    public static List<string> GetAssetDependencies()
    {

        List<string> dependencies = new List<string>();

        List<string> selectedAssets = GetAllSelectedAssets();

        for (int i = 0; i < selectedAssets.Count; i++)
        {
            string[] deps = AssetDatabase.GetDependencies(selectedAssets[i], true);
            foreach (string depName in deps)
            {
                Debug.Log(depName);
            }
        }

        return dependencies;
    }
    public static void GetCurrentDeirctoryAllAssets()
    {
        if (_AssetBundleDirectory != null)
        {
            string directoryPath = AssetDatabase.GetAssetPath(_AssetBundleDirectory);
            CurrentAllAssets = FindAllAssetNameFromDirectory(directoryPath);
            CurrentSelectAssets = new bool[CurrentAllAssets.Count];
        }
    }
    static BuildAssetBundleOptions CheckCompressionPattern()
    {
        BuildAssetBundleOptions option = new BuildAssetBundleOptions();
        switch ((CompressionPattern))
        {
            case AssetBundleCompresionPattern.LZMA:
                option = BuildAssetBundleOptions.None;
                break;
            case AssetBundleCompresionPattern.LZ4:
                option = BuildAssetBundleOptions.ChunkBasedCompression;
                break;
            case AssetBundleCompresionPattern.None:
                option = BuildAssetBundleOptions.UncompressedAssetBundle;
                break;
        }
        return option;
    }





    //窗口类
    [MenuItem(nameof(AssetManager) + "/" + nameof(OpenAssetManagerWindow))]
    static void OpenAssetManagerWindow()
    {
        //Rect windowRect = new Rect(0, 0, 500, 500);

        AssetManagerEditorWindow window = (AssetManagerEditorWindow)EditorWindow.GetWindow(typeof(AssetManagerEditorWindow));
    }







    static void CheckBuildingOutputPath()
    {
        switch (BuildingPattern)
        {
            case AssetBundlePattern.EditorSimuLation:
                break;

            case AssetBundlePattern.Local:
                AssetBundleOutputPath = Path.Combine(Application.streamingAssetsPath,"Local",AssetBundleLoad.MainAssetsBundleName);

                break;
            case AssetBundlePattern.Remote:
                AssetBundleOutputPath = Path.Combine(Application.persistentDataPath, "Remote",AssetBundleLoad.MainAssetsBundleName);
                break;
        }
        if (string.IsNullOrEmpty(AssetBundleOutputPath))
        {
            Debug.Log("dsadas");
        }
        else if (!Directory.Exists(AssetBundleOutputPath))
        {
            Directory.CreateDirectory(AssetBundleOutputPath);
        }
    }

    public static List<GUID> ContrastDependenciesFromGUID(List<GUID> setsA, List<GUID> setsB)
    {
        List<GUID> newDependencies = new List<GUID>();
        //    
        foreach (var assetGUID in setsA)
        {
            if (setsB.Contains(assetGUID))
            {
                newDependencies.Add(assetGUID);
            }
        }
        // 
        foreach (var assetGUID in newDependencies)
        {
            if (setsA.Contains(assetGUID))
            {
                setsA.Remove(assetGUID);
            }
            if (setsB.Contains(assetGUID))
            {
                setsB.Remove(assetGUID);
            }
        }
        return newDependencies;
    }
    public static void BuildAssetBundleFromSets()
    {
        CheckBuildingOutputPath();

        if (AssetBundleDirectory == null)
        {
            Debug.LogError("ada");
            return;
        }
 
        List<string> selectedAssets = GetAllSelectedAssets();


        List<List<GUID>> selectedAssetsDependcies = new List<List<GUID>>();

        foreach (string selectedAsset in selectedAssets)
        {
            string[] assetDeps = AssetDatabase.GetDependencies(selectedAsset, true);

            List<GUID> assetGUIDs = new List<GUID>();

            foreach (string assetdep in assetDeps)
            {
                GUID assetGUID = AssetDatabase.GUIDFromAssetPath(assetdep);
                assetGUIDs.Add(assetGUID);
            }
            selectedAssetsDependcies.Add(assetGUIDs);
        }
        for (int i = 0; i < selectedAssets.Count; i++)
        {
            int nextIdex = i + 1;
            if (nextIdex >= selectedAssetsDependcies.Count)
            {
                break;
            }
    
            for (int j = 0; j <= i; j++)
            {

                List<GUID> newDerivedAssets = ContrastDependenciesFromGUID(selectedAssetsDependcies[j], selectedAssetsDependcies[nextIdex]);
                if (newDerivedAssets.Count > 0 && newDerivedAssets != null)
                {
                    selectedAssetsDependcies.Add(newDerivedAssets);
                }
            }

        }

        AssetBundleBuild[] assetBundleBuilds = new AssetBundleBuild[selectedAssets.Count];
        for (int i = 0; i < assetBundleBuilds.Length; i++)
        {
            assetBundleBuilds[i].assetBundleName = i.ToString();
            string[] assetNames = new string[selectedAssetsDependcies[i].Count];
            for (int j = 0; j < assetNames.Length; j++)
            {
                if (assetNames.Contains(".cs"))
                {
                    continue;
                }
                string assetName = AssetDatabase.GUIDToAssetPath(selectedAssetsDependcies[i][j].ToString());
                assetNames[j] = assetName;
            }
            assetBundleBuilds[i].assetNames = assetNames;
        }
        BuildPipeline.BuildAssetBundles(AssetBundleOutputPath, assetBundleBuilds, CheckCompressionPattern(),
          BuildTarget.StandaloneWindows);

        AssetDatabase.Refresh();

    }

    public static void BuildAssetBundleFromEditorWindow()
    {
        CheckBuildingOutputPath();

        if (AssetBundleDirectory == null)
        {
            Debug.LogError("   Ŀ¼      ");
            return;
        }

        List<string> selectedAssets = GetAllSelectedAssets();


        AssetBundleBuild[] assetBundleBuilds = new AssetBundleBuild[selectedAssets.Count];

        string directoryPath = AssetDatabase.GetAssetPath(_AssetBundleDirectory);

        for (int i = 0; i < assetBundleBuilds.Length; i++)
        {
            string bundleName = selectedAssets[i].Replace($@"{directoryPath}\", string.Empty);



            bundleName = bundleName.Replace(".prefab", string.Empty);

            assetBundleBuilds[i].assetBundleName = bundleName;

            assetBundleBuilds[i].assetNames = new string[] { selectedAssets[i] };
        }

        BuildPipeline.BuildAssetBundles(AssetBundleOutputPath, assetBundleBuilds, CheckCompressionPattern(),
            BuildTarget.StandaloneWindows);
        Debug.Log(AssetBundleOutputPath);

        AssetDatabase.Refresh();
    }




    public static List<string> FindAllAssetNameFromDirectory(string directoryPath)
    {

        List<string> assetPaths = new List<string>();

        if (string.IsNullOrEmpty(directoryPath) || !UnityEngine.Windows.Directory.Exists(directoryPath))
        {
            Debug.Log("文件夹路径不存在");
            return null;
        }
        //
        DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath);



        FileInfo[] fileInfos = directoryInfo.GetFiles();

        foreach (FileInfo info in fileInfos)
        {
            if (info.Extension.Contains(".meta"))
            {
                continue;
            }
            string assetPath = Path.Combine(directoryPath, info.Name);
            assetPaths.Add(assetPath);
            Debug.Log($"{assetPath}");
        }
        return assetPaths;
    }


    static bool IsValidExtentionName(string fileName)
    {
        bool isValid = true;
        foreach (string invalidName in InvalidExtentionName)
        {
            if (fileName.Contains(invalidName))
            {
                isValid = false;
            }
        }
        return isValid;
    }
    static List<string> FindAllAssetPathFromFolder(string directoryPath)
    {
        List<string> objectPaths = new List<string>();
        if (!Directory.Exists(directoryPath))
        {
            return null;
        }
        DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath);
        FileInfo[] fileInfos = directoryInfo.GetFiles();
        for (int i = 0; i < fileInfos.Length; i++)
        {
            var file = fileInfos[i];

            if (!IsValidExtentionName(file.Extension))
            {
                continue;
            }

            string path = $"{directoryPath}/{file.Name}";
            objectPaths.Add(path);
        }
        return objectPaths;
    }
    public static void GetNodesFromDependencies(AssetBundleNode lastNode, List<AssetBundleNode> currentAllNodes)
    {
        string[] assetNames = AssetDatabase.GetDependencies(lastNode.assetName, false);
        if (assetNames.Length > 0)
        {
            lastNode.OutEdge = new AssetBundleEdge();
        }
        foreach (string assetName in assetNames)
        {
            if (!IsValidExtentionName(assetName))
            {
                continue;
            }
            AssetBundleNode currentNode = null;
            foreach (AssetBundleNode existingNode in currentAllNodes)
            {
                if (existingNode.assetName == assetName)
                {
                    currentNode = existingNode;
                    break;
                }
            }
            if (currentNode == null)
            {
                currentNode = new AssetBundleNode();
                currentNode.assetName = assetName;
                currentNode.InEdge = new AssetBundleEdge();
                currentAllNodes.Add(currentNode);
            }
            if (lastNode != null)
            {
                currentNode.InEdge.Nodes.Add(lastNode);
                if (lastNode.SourceIndex >= 0)
                {
                    currentNode.SourceIndices.Add(lastNode.SourceIndex);
                }
                else
                {
                    currentNode.SourceIndices = lastNode.SourceIndices;
                }

                lastNode.OutEdge.Nodes.Add(currentNode);
            }
            //  ȡ        Node
            GetNodesFromDependencies(currentNode, currentAllNodes);
        }


    }
}



